home *** CD-ROM | disk | FTP | other *** search
- /* --------------------------------------------------------------------
- * NAME
- * Emit V2.0 by Justin V. McCormick 1/29/88
- *
- * Placed by the Author in the Public Domain -- freely reproducable
- * in any form so long as this notice remains intact.
- *
- * Some Rights Reserved (i.e., I will not be held accountable for the
- * use or misuse of this code).
- *
- * SYNOPSIS
- * emit -[s][r] [filename]
- * -s Send filename over serial port
- * -r Receive data from serial port and store in "filename"
- *
- * DESCRIPTION
- * Emit is a high-speed serial transfer utility useful for moving
- * files from one Amiga to another via a null modem cable. Emit
- * operates at 280,000 baud, and its actual data transfer rate has
- * been benchmarked at over 24,000 bytes per second.
- *
- * You should type "emit -s foo" on the sending Amiga to transmit
- * a file named "foo". The sending Amiga will attempt to access
- * the file "foo", and await a handshake signal from the receiving
- * Amiga. On the receiving Amiga, you should then type "emit -r foo"
- * to download the file. As soon as the handshake has been
- * established, transfer of the file begins. You can use Control-C
- * to break out of the program during execution.
- *
- * The idea behind Emit was to create a useful tool for downloading
- * code to a target machine for testing. For instance, in my
- * development enviroment, I compile my code on a Amiga 1000, then
- * zap the code over the serial port to an Amiga 500 for testing.
- * If the code bombs and the Guru visits, no sweat; my environment
- * on the 1000 is safe (sorta, I still use vdk: and make lots of
- * intermediate backups!). The benefits of a dual-machine development
- * enviroment are terrific: I insure that the code works on both the
- * 1000 and the 500, I have a fairly safe development environment,
- * and I can "Wack" the code through the port using the 1000 as a
- * debugging terminal when the going gets tough. I would estimate
- * that moving to a dual-machine setup has doubled the efficiency
- * of my development time. I use to do a lot of disk swapping
- * before writing Emit; I propose that this program will greatly
- * increase the lifespans of my disk-eject mechanisms.
- *
- * Emit was written very quick-and-dirty. It shows. I wrote it as
- * an experiment to see how fast Exec can handle the serial port,
- * and because there were no programs available that did what I
- * needed. For instance, I tried using several terminal programs,
- * both commercial and public domain. Only Online! 2.0 would perform
- * reliably at 19200 baud, and I felt ridiculous running a 176K
- * program to download a measly couple of bytes. (BTW, Diga! NEVER
- * managed to do better than 4800 baud on ANY setting, according
- * to my stop-watch). Also, the RKM 1 mentions that baud rates
- * up to 292,000 may be specified (though your mileage may vary),
- * so I was itching to try it.
- *
- * Note that this code is *NOT* to be taken as a proper example of
- * how to control the serial port -- it merely works for me. I do
- * several deplorable things for the sake of speed like busy-waiting,
- * disabling interrupts, poking and peeking at hardware registers,
- * and making unwarrented assumptions about the hardware that
- * SHOULD NEVER be done if you want your code to work with
- * future versions of the Amiga hardware.
- *
- * FILES
- * Emit * The executable code
- * emit.c * Source to main routine, control module
- * ser.asm * Source to assembly serial read/write routines
- * aprintf.asm * Hand-rolled printf() replacement
- * macros.i * Assembly includes
- * i.pre.h * A kitchen-sink include file
- * makefile * Makes it
- *
- * Written using Manx Aztec C 3.4b (thanks Jim!)
- * You might want to precompile i.pre.h to speed compiling.
- *
- * DIAGNOSTICS
- * Emit has fairly good error detection and recovery facilities.
- * Typical problems that could crop up are bad filenames, file not
- * found, inability to open a file, insufficient RAM, serial port
- * problems, and serial flow control difficulties. If a problem is
- * detected, Emit will exit and describe the problem, possibly with
- * a standard AmigaDOS error number or Exec serial problem number.
- *
- * BUGS
- * Undoubtably. Most of the known problems exist on the receive side
- * of the program. The Emit sender sends the filesize to the Emit
- * receiver; the receiver gets only one chance to grab this
- * critical information. No error-detection/correction/checksumming
- * is done on transfered data. When receiving files, Emit creates
- * an input buffer the same size as the file to be downloaded; if
- * there is insufficient RAM for the buffer the transfer will fail.
- * I put an arbitrary 500K limit on the filesize that can be
- * downloaded -- feel free to change this if it annoys you.
- * I am being rather rude, crude, and high-handed when I access
- * the serial port, and while the program has proven reliable for
- * me, I can't promise there isn't some problem in there that might
- * bite one day.
- *
- * Any comments, improvements, or discussion of techniques are
- * welcome. You can reach me at:
- *
- * Justin V. McCormick
- * 100 S. Meyers, Apt #1723
- * Lafayette, LA 70508
- *
- * Voice: 1-318-981-1314
- * PLINK: progress**
- * BIX: dbrowning
- *
- * -------------------------------------------------------------------- */
- #include "i.pre.h"
-
- #define BAUD 280000L /* Baud Rate */
- #define BUFSIZE 8192L /* Size of output buffer */
-
- /* Externs */
- extern int Enable_Abort; /* We want to process ^C ourself */
-
- /* Global functions */
- char *extractfilename();
-
- /* Global variables */
- int updownflag = 0; /* 0 for upload, 1 for download */
- char filename[300]; /* Filename string */
- char progname[34]; /* What the program is called */
- long filesize; /* Byte length of file to upload */
-
- /* File related variables */
- struct FileHandle *emitfile = 0L; /* Pointer to stream opened */
- struct FileInfoBlock *eFib = 0L; /* General purpose Fib pointer */
- struct FileLock *eLock = 0L; /* FileLock pointer */
- long buffersize = 0L; /* Size of input buffer */
- UBYTE *outbuf = 0L; /* Pointer to input buffer */
-
- /* STUFF FOR SERIAL IO */
- #define SRPORTNAME "SerReadPort"
- #define SWPORTNAME "SerWritePort"
- ULONG S_Sig; /* Signal masks for ports */
-
- /* globals functions */
- int OpenSerial ();
- int CloseSerial ();
-
- /* global structures */
- struct IOExtSer *RReq = 0L; /* Pointer to Read Request block */
- struct IOExtSer *WReq = 0L; /* Pointer to Write Request block */
- struct MsgPort *SerRPort = 0L; /* Serial Read Port */
- struct MsgPort *SerWPort = 0L; /* Serial Write Port */
-
- /* other globals */
- long error = 0L; /* global error return status */
- ULONG openflags = 0L; /* devices we opened */
- UBYTE aserinbuf[2]; /* asynchronous serial input buffer */
- UBYTE *inbuf = 0L; /* private serial input buffer */
- struct FileHandle *cliout; /* Standard output filehandle */
-
- /* openflags defines */
- #define SRDEV_OPENED 1 /* set if opened read device */
- #define SWDEV_OPENED 2 /* set if opened write device */
-
- /* -------------------------------------------------------------------- */
- /* MAIN CODE */
- /* -------------------------------------------------------------------- */
- main (argc, argv)
- int argc;
- char **argv;
- {
- register long status;
- register long x;
-
- /* Do our own ^C processing, thank you */
- Enable_Abort = 0;
-
- /* Where's output go? */
- cliout = Output();
- if (cliout == 0L)
- CleanUp("");
-
- /* Allocate a FileInfoBlock */
- if ( (eFib = (struct FileInfoBlock *)AllocMem((long)sizeof(struct FileInfoBlock), MEMF_PUBLIC | MEMF_CLEAR)) == 0L)
- CleanUp("No RAM");
-
- /* Parse command line arguments */
- strcpy(progname, extractfilename(argv[0]));
-
- if (argc < 3)
- ShowUsage(); /* Not enough args */
-
- if (argv[1][0] != '-')
- ShowUsage(); /* Option didn't start with a '-' */
-
- /* Upload or Download? */
- switch (argv[1][1])
- {
- case 's':
- case 'S':
- updownflag = 0;
- break;
- case 'r':
- case 'R':
- updownflag = 1;
- break;
- default:
- ShowUsage();
- break;
- }
-
- /* Copy off argv[2], assume it is filename */
- strcpy(filename, argv[2]);
-
- /* Open file */
- if (updownflag == 1) /* Create and open file for download */
- emitfile = Open(filename, MODE_NEWFILE);
- else
- {
- /* Allocate input buffer */
- if ( (inbuf = (UBYTE*)AllocMem(BUFSIZE, MEMF_PUBLIC|MEMF_CLEAR)) == 0L)
- CleanUp("No ram for buffer!");
-
- /* Grab a Lock the file */
- if ( (eLock = (struct FileLock *)Lock(filename, ACCESS_READ)) == 0L)
- {
- CleanUp("can't lock '%s', IoErr %ld", filename, IoErr());
- }
-
- /* Examine the root block */
- if ( !Examine(eLock, eFib) )
- {
- CleanUp("can't examine '%s', IoErr %ld", filename, IoErr());
- }
-
- /* Is it a file? */
- if (eFib->fib_DirEntryType >= 0L)
- {
- CleanUp("'%s' is a directory, not a file", filename);
- }
-
- /* Grab the length */
- filesize = eFib->fib_Size;
-
- /* Open the file for upload */
- emitfile = Open(filename, MODE_OLDFILE);
-
- }
-
- if (emitfile == 0L)
- {
- CleanUp("can't open '%s', IoErr %ld", filename, IoErr() );
- }
-
- /* Do heavy Amiga Kernal Magic to set up serial I/O */
- OpenSerial();
-
- /* Do the first async read to initialize the read port */
- GetASer();
-
- /* Enter the control loop */
- if (updownflag == 0) /* Upload */
- do_upload();
- else /* Download */
- do_download();
-
- /* Must be done */
- CleanUp("done");
- }
-
- /* -------------------------------------------------------------------- */
- /* Describe correct usage, cleanup */
- /* -------------------------------------------------------------------- */
- ShowUsage()
- {
- aprintf("\x9b1;33;40mEmit V2.0\x9b0m -- 280,000 Baud Serial File Transfer Utility\n\
- \tFreeware by Justin V. McCormick 1/29/88\n\n\
- \x9b1;33;40mUsage:\x9b0m %s -[s][r] [filename]\n\
- \t-s == send\n\
- \t-r == receive\n", progname);
- CleanUp("");
- }
-
- /*---------------------------- CleanUp -----------------------------------*/
- /* Close up everything and exit.
- */
- CleanUp (msg, arg1, arg2, arg3)
- char *msg, *arg1, *arg2, *arg3;
- {
- if (inbuf)
- FreeMem(inbuf, BUFSIZE);
-
- if (eLock)
- UnLock(eLock);
-
- if (eFib)
- FreeMem(eFib, (long)sizeof(struct FileInfoBlock));
-
- if (outbuf)
- FreeMem(outbuf, buffersize);
-
- if (emitfile)
- Close(emitfile);
-
- CloseSerial ();
-
- if (*msg)
- {
- aprintf ("\x9b1;33;40m%s:\x9b0m ", progname);
- aprintf(msg, arg1, arg2, arg3);
- aprintf(".\n");
- }
-
- exit (0);
- }
-
- /* -------------------------------------------------------------------- */
- /* Given a full pathname, return just the filename */
- /* -------------------------------------------------------------------- */
- char *extractfilename(name)
- register char *name;
- {
- register char *tmp;
-
- tmp = rindex(name, '/');
- if (!tmp)
- {
- tmp = rindex(name, ':');
- if (!tmp)
- tmp = name;
- else
- tmp++;
- }
- else
- tmp++;
- if (strlen(tmp) > 30)
- tmp[30] = '\0';
- return(tmp);
- }
-
- /* -------------------------------------------------------------------- */
- /* Override Manx declaration, we will process ^C ourself */
- /* -------------------------------------------------------------------- */
- Chk_Abort ()
- {
- return (0);
- }
-
- /* -------------------------------------------------------------------- */
- /* See if user hit ^C */
- /* -------------------------------------------------------------------- */
- CheckCntrlC()
- {
- if (SetSignal (0L, 0L) & SIGBREAKF_CTRL_C)
- {
- aprintf("^C");
- return (1);
- }
- else
- return(0);
- }
-
- /* -------------------------------------------------------------------- */
- /* Upload a file asap */
- /* -------------------------------------------------------------------- */
- do_upload()
- {
- register long length;
- long blksize = BUFSIZE;
-
- /* Let the user in on what's happening */
- aprintf("Filesize to upload: %ld bytes\n", filesize);
- aprintf("Waiting for ENQ...");
- fflush(stdout);
-
- /* Might be some trash or ENQ's already, flush it out */
- RReq->IOSer.io_Command = CMD_FLUSH;
- if (DoIO (RReq) != 0L)
- CleanUp ("Can't flush the serial device!");
-
- /* Wait for ENQ character from dest */
- while (1)
- {
- if (GotENQ()) /* Check for ENQ */
- break;
- if (CheckCntrlC())
- CleanUp("upload aborted");
- Delay(5L);
- }
- SerialWrite("\x06", 1L); /* Send a ACK */
-
- aprintf("ENQ\nSending file...\n");
- Delay(15L);
-
- SerialWrite(&filesize, 4L);
-
- Delay(25L);
- aprintf("%ld\n", filesize);
-
- /* Dump the file */
- while (filesize)
- {
- if (filesize < blksize)
- blksize = filesize;
-
- length = Read(emitfile, inbuf, blksize);
- if (length != blksize)
- {
- CleanUp("can't read file, IoErr %ld", IoErr());
- }
-
- SerialWrite(inbuf, blksize);
-
- filesize = filesize - blksize;
- aprintf("\x9b\x46\x0d\x9b\x4b%ld\n", filesize);
- if (CheckCntrlC())
- CleanUp("upload aborted");
- }
- }
-
- /* -------------------------------------------------------------------- */
- /* Download a file pronto */
- /* -------------------------------------------------------------------- */
- do_download()
- {
- register long length;
- register UBYTE *bufp;
- register long blksize;
-
- aprintf("Waiting for ACK...");
- fflush(stdout);
-
- /* Enquire of host at regular intervals, wait for ACK or abort */
- while (1)
- {
- if (GotACK()) /* Got an ACK */
- break;
-
- if (CheckCntrlC()) /* Got an abort */
- CleanUp("Aborted");
-
- SerialWrite ("\x05", 1L); /* Otherwise, send an ENQ */
- Delay (10L); /* wait 1/5th sec. */
- }
- aprintf("ACK\n");
-
- /* Get filesize to download */
- SerialRead(&filesize, 4L);
- if (filesize <= 0 || filesize > 500000L)
- {
- CleanUp("Filesize %ld out of range", filesize);
- }
- aprintf("Filesize to download: %ld bytes\n Reading data...\n", filesize);
- fflush(stdout);
-
- /* Allocate input buffer */
- buffersize = filesize;
- if ( (outbuf = AllocMem(buffersize, MEMF_PUBLIC | MEMF_CLEAR)) == 0L)
- CleanUp("can't allocate input buffer");
-
- /* Pull in the rest of the characters */
- SerialRead(outbuf, filesize);
-
- /* Write the buffer to file */
- aprintf(" Writing file...\n");
- Write(emitfile, outbuf, buffersize);
- }
-
- /* -------------------------- OpenSerial ------------------------------ */
- /* Open the serial device twice, once for read and once for write. */
- /* -------------------------------------------------------------------- */
- int OpenSerial ()
- {
-
- /* Open the ports */
- if ((SerRPort = CreatePort (SRPORTNAME, 0L)) == 0L ||
- (SerWPort = CreatePort (SWPORTNAME, 0L)) == 0L)
- {
- CleanUp ("can't open SerRPort or SerWPort");
- }
-
- /* Create the request blocks */
- if ((RReq = (struct IOExtSer *) CreateExtIO (SerRPort, (long) sizeof (*RReq))) == 0L ||
- (WReq = (struct IOExtSer *) CreateExtIO (SerWPort, (long) sizeof (*WReq))) == 0L)
- {
- CleanUp ("can't open RReq or WReq");
- }
-
- /* Set io_SerFlags to share them ports */
- RReq->io_SerFlags = WReq->io_SerFlags = SERF_SHARED;
-
- /* Open the device once for each request block */
- if ((error = OpenDevice (SERIALNAME, 0L, RReq, 0L)) != 0L)
- CleanUp ("can't open read device!");
- else
- openflags |= SRDEV_OPENED; /* Successfully opened read device */
-
- if ((error = OpenDevice (SERIALNAME, 0L, WReq, 0L)) != 0L)
- CleanUp ("can't open write device!");
- else
- openflags |= SWDEV_OPENED; /* Successfully opened write device */
-
- /* Set our wakeup signal mask for the serial read port */
- S_Sig = 1L << SerRPort->mp_SigBit;
-
- /* Set serial parameters */
- RReq->io_Baud = BAUD;
- WReq->io_Baud = BAUD;
- RReq->io_RBufLen = 8000L;
- WReq->io_RBufLen = 8000L;
- RReq->io_ReadLen = (UBYTE)8;
- WReq->io_ReadLen = (UBYTE)8;
- RReq->io_WriteLen = (UBYTE)8;
- WReq->io_WriteLen = (UBYTE)8;
- RReq->io_StopBits = (UBYTE)1;
- WReq->io_StopBits = (UBYTE)1;
- RReq->io_SerFlags = SERF_SHARED | SERF_RAD_BOOGIE;
- WReq->io_SerFlags = SERF_SHARED | SERF_RAD_BOOGIE;
- RReq->IOSer.io_Command = SDCMD_SETPARAMS;
- WReq->IOSer.io_Command = SDCMD_SETPARAMS;
- if (DoIO(RReq) || DoIO(WReq))
- CleanUp("can't set serial parameters");
-
- }
-
- /*----------------------------- CloseSerial -------------------------------*/
- int CloseSerial ()
- {
- struct IOExtSer *tReq;
-
- if (RReq)
- {
- if (openflags & SRDEV_OPENED)
- {
- while ((tReq = (struct IOExtSer *) CheckIO (RReq)) == 0L)
- {
- AbortIO (RReq);
- WaitIO (RReq);
- GetMsg (SerRPort);
- }
- CloseDevice (RReq);
- }
- DeleteExtIO (RReq, (long)sizeof(*RReq) );
- }
-
- if (WReq)
- {
- if (openflags & SWDEV_OPENED)
- CloseDevice (WReq);
- DeleteExtIO (WReq, (long)sizeof(*WReq) );
- }
-
- if (SerRPort)
- DeletePort (SerRPort);
- if (SerWPort)
- DeletePort (SerWPort);
- }
-
- int GotACK()
- {
- struct IOExtSer *tReq;
- register char tempchar;
-
- if ((tReq = (struct IOExtSer *) CheckIO (RReq)) == 0L)
- return (0L);
-
- WaitIO (RReq);
- tempchar = aserinbuf[0] & '\x7f';
- GetASer();
-
- if (tempchar == '\x06')
- return(1);
- else
- return(0);
- }
-
- int GotENQ()
- {
- struct IOExtSer *tReq;
- register char tempchar;
-
- if ((tReq = (struct IOExtSer *) CheckIO (RReq)) == 0L)
- return (0L);
-
- WaitIO (RReq);
- tempchar = aserinbuf[0] & '\x7f';
- GetASer();
-
- if (tempchar == '\x05')
- return(1);
- else
- return(0);
- }
-
- /* --------------------------------- GetASer -------------------------- */
- /* Send a async read request to the serial port for 1 character */
- /* -------------------------------------------------------------------- */
- GetASer ()
- {
- RReq->IOSer.io_Command = CMD_READ;
- RReq->IOSer.io_Length = 1L;
- RReq->IOSer.io_Data = (APTR) &aserinbuf[0];
- SendIO (RReq);
- }
-